package com.zhangfb95.crosschain.plugin.container.java.fabric2.service.chaincodeevent;

import com.zhangfb95.crosschain.infra.model.Result;
import com.zhangfb95.crosschain.plugin.container.java.fabric2.infra.data.mapper.Fabric2BlockEventMapper;
import com.zhangfb95.crosschain.plugin.container.java.fabric2.infra.data.mapper.Fabric2TxEventMapper;
import com.zhangfb95.crosschain.plugin.container.java.fabric2.infra.model.enums.EventHandleStatusEnum;
import com.zhangfb95.crosschain.plugin.container.java.fabric2.infra.model.mapper.entity.Fabric2TxEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author zhangfubing
 * @since 2024/1/10
 */
@Slf4j
public abstract class AbstractChaincodeEventHandler<T> implements ChaincodeEventHandler {

    @Autowired
    protected Fabric2BlockEventMapper fabric2BlockEventMapper;
    @Autowired
    protected Fabric2TxEventMapper fabric2TxEventMapper;

    @Override
    public void handle(Fabric2TxEvent fabric2TxEvent) {
        // 考虑分布式乐观锁

        // 预处理，失败时直接返回
        if (!beforePre(fabric2TxEvent)) {
            return;
        }
        Result<T> preResult = pre(fabric2TxEvent);
        if (!preResult.isSuccess()) {
            fail(fabric2TxEvent);
            return;
        }

        // 处理，失败时直接返回
        Result<T> handleResult = handle(fabric2TxEvent, preResult);
        if (!handleResult.isSuccess()) {
            fail(fabric2TxEvent);
            return;
        }

        // 处理后逻辑
        success(fabric2TxEvent);
        post(fabric2TxEvent, handleResult);
    }

    protected boolean beforePre(Fabric2TxEvent fabric2TxEvent) {
        if (fabric2TxEvent.getId() != null) {
            return EventHandleStatusEnum.shouldHandle(fabric2TxEvent.getHandleStatus());
        }

        if (fabric2TxEventMapper.insertIgnore(fabric2TxEvent) > 0) {
            return true;
        }

        String chainName = fabric2TxEvent.getChainName();
        String channelName = fabric2TxEvent.getChannelName();
        String txId = fabric2TxEvent.getTxId();
        Fabric2TxEvent dbFabric2TxEvent = fabric2TxEventMapper.getByTxId(chainName, channelName, txId);
        if (dbFabric2TxEvent == null) {
            log.error("dbFabric2TxEvent not find, txId:" + txId);
            return false;
        }

        fabric2TxEvent.setId(dbFabric2TxEvent.getId());
        return EventHandleStatusEnum.shouldHandle(dbFabric2TxEvent.getHandleStatus());
    }

    protected Result<T> pre(Fabric2TxEvent fabric2TxEvent) {
        return Result.ok();
    }

    protected abstract Result<T> handle(Fabric2TxEvent fabric2TxEvent, Result<T> preAfterResult);

    protected void post(Fabric2TxEvent fabric2TxEvent, Result<T> result) {
    }

    private void success(Fabric2TxEvent fabric2TxEvent) {
        fabric2TxEventMapper.success(fabric2TxEvent.getId());
    }

    private void fail(Fabric2TxEvent fabric2TxEvent) {
        fabric2TxEventMapper.fail(fabric2TxEvent.getId());
    }
}
